package cn.ztuo.bitrade.QRCode;

import cn.ztuo.bitrade.config.AliyunConfig;
import cn.ztuo.bitrade.service.LocaleMessageSourceService;
import cn.ztuo.bitrade.util.GeneratorUtil;
import cn.ztuo.bitrade.util.MessageResult;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.OSSObject;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
@Slf4j
@Component
public class QRCodeUtils {

    // 二维码宽度
    private static Integer WIDTH_PIX = 300;

    // 二维码高度
    private static Integer HEIGHT_PIX = 300;

    // 生成文件类型
    private static String TYPE = "png";

    // 编码格式
    private static String CHAR_TYPE = "UTF-8";

    /**
     * 二维码填充颜色 BEGIN
     */
    // 黑色
    private static Integer COLOR_BLACK = 0XFF000000;
    // 白色 0xFF是补码
    private static Integer COLOR_WHITE = 0XFFFFFFFF;
    // 红色
    private static Integer COLOR_RED = 0xFFFF0000;
    // 蓝色 ,0xFF  #FFD700
    private static Integer COLOR_BLUE = 0xFFFFD700;
    /** 二维码填充颜色 END*/

    /**
     * 字体设定 BEGIN
     */
    //设定写在二维码上的字体 样式
    private static String FONT_NAME = "Consolas";

    //字体颜色
    private static Color FONT_COLOR = Color.gray;

    //字体 大小
    private static Integer FONT_SIZE = 12;
    /**字体设定 END*/


    @Autowired
    private AliyunConfig aliyunConfigDouble;
    @Autowired
    private LocaleMessageSourceService sourceServiceDouble;

    private static LocaleMessageSourceService sourceService;
    private static AliyunConfig aliyunConfig;
    @PostConstruct
    public void init() {
        aliyunConfig = aliyunConfigDouble;
        sourceService = sourceServiceDouble;
    }

    /**
     * 读取二维码的文件里面的信息
     *
     * @param filePath
     * @return
     * @throws Exception
     */
    public static String readQRImg(String filePath) throws Exception {
        // 读取图片
        BufferedImage image = ImageIO.read(new File(filePath));

        LuminanceSource source = new BufferedImageLuminanceSource(image);
        Binarizer binarizer = new HybridBinarizer(source);
        BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
        Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
        hints.put(DecodeHintType.CHARACTER_SET, CHAR_TYPE);
        Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码

        return result.getText();

    }

    /**
     * 通过BufferedImage 来写入图片的操作,没有用到Google 的j2se包
     *
     * @param text     写入的信息
     * @return
     * @throws Exception
     */
    public static String writeQRImg(String text, String... logoPath) throws Exception {
        // 配置参数
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        // 字符编码
        hints.put(EncodeHintType.CHARACTER_SET, CHAR_TYPE);

        // 容错级别
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

        // 设置空白边距的宽度
        hints.put(EncodeHintType.MARGIN, 0); // 默认是4

        // 1、生成二维码
        BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, WIDTH_PIX, HEIGHT_PIX, hints);

        // 2、获取二维码宽高
        int codeWidth = bitMatrix.getWidth();
        int codeHeight = bitMatrix.getHeight();

        // 3、将二维码放入缓冲流
        BufferedImage image = new BufferedImage(codeWidth, codeHeight, BufferedImage.TYPE_INT_RGB);

        for (int i = 0; i < codeWidth; i++) {
            for (int j = 0; j < codeHeight; j++) {
                // 4、循环将二维码内容定入图片
                //判断 BitMatrix 是否存在像素
                image.setRGB(i, j, bitMatrix.get(i, j) ? COLOR_BLACK : COLOR_WHITE);
            }
        }

        //判断是否写入logo图片 
        if (logoPath != null && logoPath.length > 0) {
            File logoPic = new File(new URI(logoPath[0]));
            if (logoPic.exists()) {
                //在原来基础上，再添加一个图片
                Graphics2D g = image.createGraphics();
                BufferedImage logo = ImageIO.read(logoPic);
                int widthLogo = logo.getWidth(null) > image.getWidth() * 2 / 10 ?
                        (image.getWidth() * 2 / 10) : logo.getWidth(null);
                int heightLogo = logo.getHeight(null) > image.getHeight() * 2 / 10 ?
                        (image.getHeight() * 2 / 10) : logo.getHeight(null);

                //设定在图片中间
                int x = (image.getWidth() - widthLogo) / 2;
                int y = (image.getHeight() - heightLogo) / 2;

                // 开始绘制图片  
                g.drawImage(logo, x, y, widthLogo, heightLogo, null);

                //绘制圆角矩形
                g.drawRoundRect(x, y, widthLogo, heightLogo, 15, 15);

                //边框宽度  
                g.setStroke(new BasicStroke(2));

                //边框颜色  
                g.setColor(Color.WHITE);
                g.drawRect(x, y, widthLogo, heightLogo);

                //最后一个参数用来设置字体的大小
                Font fontStyle = new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);

                //设定颜色
                g.setColor(FONT_COLOR); //设定颜色
                g.setFont(fontStyle); //设定字体
                Integer fontStartIndex = image.getWidth() - (int) (text.length() * FONT_SIZE * 0.7);
                g.drawString(text, fontStartIndex, image.getHeight() - FONT_SIZE);
                //释放图像资源
                g.dispose();
                logo.flush();
                image.flush();
            }
        }
        return uploadOSS(image);
    }

    /**
     * 获取二维码图片
     *
     * @param filePath 图片写出的路径比如 C:/ 或者 D:/test/xx
     * @return
     * @throws Exception
     */
    private static File getQRImgFile(String filePath) throws Exception {
        // 获取图片名称
        String fileName = UUID.randomUUID().toString() + "." + TYPE;
        File outPutImage = new File(filePath + File.separator + fileName);

        // 如果图片不存在创建图片
        if (!outPutImage.exists()) {
            outPutImage.createNewFile();
        }
        return outPutImage;
    }
    /**
     * 获取二维码图片二进制流
     *
     * @param url 图片oss 路径
     * @return
     * @throws Exception
     */
    public static InputStream getQRImgInuptStream(String url) throws Exception {
        // 获取图片名称
        OSSClient ossClient = new OSSClient(aliyunConfig.getOssEndpoint(), aliyunConfig.getAccessKeyId(), aliyunConfig.getAccessKeySecret());
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(aliyunConfig.getOssBucketName(), "20200606140141db8af3cfa35b43bb935a463d7d61b111.png", HttpMethod.GET);
        // 设置过期时间。
        request.setExpiration(expiration);
        // 生成签名URL（HTTP GET请求）。
        URL signedUrl = ossClient.generatePresignedUrl(request);
        // 使用签名URL发送请求。
        Map<String, String> customHeaders = new HashMap<String, String>();
        // 添加GetObject请求头。
        //customHeaders.put("Range", "bytes=100-1000");
        OSSObject ossObject = ossClient.getObject(signedUrl, customHeaders);
        InputStream ins = ossObject.getObjectContent();
        return ins;
    }

    public static void main(String[] args) throws Exception {
        getQRImgInuptStream(null);
    }

    /**
     * 创建二维码，通过BitMatrix 这个类来生成二维码<br/>
     * 用到了gooogle的j2se的包
     *
     * @param text     需要写入的信息，是json数据类型.也可以是 http://yellowcong.com 等
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public static String writeQRImg4J2se(String text) throws Exception {

        // 配置参数
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        // 字符编码
        hints.put(EncodeHintType.CHARACTER_SET, CHAR_TYPE);

        // 容错级别
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

        // 设置空白边距的宽度
        hints.put(EncodeHintType.MARGIN, 0); // 默认是4

        // 图像数据转换，使用了矩阵转换 ，这种方法适合在Android机器上使用
        BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, WIDTH_PIX, HEIGHT_PIX, hints);

        // 写出成文件，设置文件地址
        // 这个是google j2se 这个包里面的api
//        MatrixToImageWriter.writeToFile(bitMatrix, TYPE, outPutImage);
        BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);

        return uploadOSS(image);
    }

    public static String uploadOSS(BufferedImage image){
        OSSClient ossClient = new OSSClient(aliyunConfig.getOssEndpoint(), aliyunConfig.getAccessKeyId(), aliyunConfig.getAccessKeySecret());
        String directory = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        try {
            String suffix = ".png";
            String key = directory + GeneratorUtil.getUUID().replaceAll("-","") + suffix;
            log.info("key:"+key);


            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
            ImageIO.write(image, "png", imOut);
            InputStream is = new ByteArrayInputStream(bs.toByteArray());


            ossClient.putObject(aliyunConfig.getOssBucketName(), key, is);
            String uri = aliyunConfig.toUrl(key);
            MessageResult mr = MessageResult.getSuccessInstance("",uri);
            mr.setData(uri);
            return mr.toString();
        } catch (OSSException oe) {
            return MessageResult.error(500, oe.getErrorMessage()).toString();
        } catch (ClientException ce) {
            log.info("Error Message: " + ce.getMessage());
            return MessageResult.error(500, ce.getErrorMessage()).toString();
        } catch (Throwable e) {
            e.printStackTrace();
            return MessageResult.error(500, sourceService.getMessage("REQUEST_FAILED")).toString();
        } finally {
            ossClient.shutdown();
        }
    }
}

